﻿/*========================== 
 * @author 郑金泉
 * @desc API配置信息类
 * ========================= */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Obsidian.Utils;

namespace Obsidian.Config
{
    public class ApiConfig
    {
        private static DateTime modified = DateTime.MinValue;
        private static ApiInfo defaultApiInfo = null;
        private static Hashtable htApiAssemblie = Hashtable.Synchronized(new Hashtable());
        private static Hashtable htApi = Hashtable.Synchronized(new Hashtable());

        public static ApiInfo DefaultApiInfo
        {
            get
            {
                CheckForChanged();
                return defaultApiInfo;
            }
        }

        public static ApiAssemblyInfo GetApiAssemblieInfo(string name)
        {
            CheckForChanged();
            if (!htApiAssemblie.ContainsKey(name))
                return null;
            return (ApiAssemblyInfo)htApiAssemblie[name];
        }

        public static ApiInfo GetApiInfo(string appKey)
        {
            if (String.IsNullOrEmpty(appKey))
                throw new Exception("参数appKey不能为空");
            CheckForChanged();
            if (!htApi.ContainsKey(appKey))
                return null;
            return (ApiInfo)htApi[appKey];
        }

        private static void CheckForChanged()
        {
            if (AppConfig.CheckForChanged() || !AppConfig.Modified.Equals(modified))
            {
                GetApiAssemblieInfos();
                GetApiInfos();
                modified = AppConfig.Modified;
            }
        }

        private static void GetApiAssemblieInfos()
        {
            lock (htApiAssemblie)
            {
                htApiAssemblie.Clear();
                XmlDocument xmlDoc = AppConfig.GetXml();
                XmlNodeList nodeList = xmlDoc.SelectNodes("appConfig/apiAssemblies/assembly");
                for (int i = 0; i < nodeList.Count; i++)
                {
                    ApiAssemblyInfo info = new ApiAssemblyInfo();
                    XmlNode node = nodeList.Item(i);
                    string val;
                    if (!XmlUtil.TryGetAttrValue(node, "name", out val))
                        throw new Exception("name属性不能为空");
                    info.SetName(val);

                    if (XmlUtil.TryGetAttrValue(node, "namespace", out val))
                        info.SetNamespace(val);

                    if (XmlUtil.TryGetAttrValue(node, "typeSuffix", out val))
                        info.SetTypeSuffix(val);

                    if (!XmlUtil.TryGetAttrValue(node, "path", out val))
                        throw new Exception("path属性不能为空");
                    info.SetPath(AppConfig.FullPath(val));

                    if (htApiAssemblie.ContainsKey(info.Name))
                        throw new Exception("存在重复配置项name：" + info.Name);

                    htApiAssemblie.Add(info.Name, info);
                }
            }
        }

        private static void GetApiInfos()
        {
            lock (htApi)
            {
                htApi.Clear();
                XmlDocument xmlDoc = AppConfig.GetXml();
                XmlNodeList nodeList = xmlDoc.SelectNodes("appConfig/apis/api");
                for (int i = 0; i < nodeList.Count; i++)
                {
                    ApiInfo info = new ApiInfo();
                    XmlNode node = nodeList.Item(i);
                    string val;
                    if (!XmlUtil.TryGetAttrValue(node, "appKey", out val) || String.IsNullOrEmpty(val))
                        throw new Exception("appKey属性不能为空");
                    info.SetAppKey(val);

                    bool bval;
                    if (XmlUtil.TryGetAttrBool(node, "isHttpRequest", out bval))
                        info.SetIsHttpRequest(bval);

                    if (XmlUtil.TryGetAttrBool(node, "needVerify", out bval))
                        info.SetNeedVerify(bval);

                    if (XmlUtil.TryGetAttrValue(node, "appSecret", out val) && !String.IsNullOrEmpty(val))
                        info.SetAppSecret(val);
                    else if (info.IsHttpRequest)
                        throw new Exception("当isHttpRequest为true时，appSecret属性不能为空");

                    if (XmlUtil.TryGetAttrValue(node, "timestampOffset", out val) && !String.IsNullOrEmpty(val))
                    {
                        int timestampOffset;
                        if (int.TryParse(val, out timestampOffset))
                        {
                            info.SetTimestampOffset(timestampOffset);
                        }
                        else
                        {
                            throw new Exception("timestampOffset属性格式错误");
                        }
                    }
                    else
                    {
                        info.SetTimestampOffset(5);
                    }

                    if (XmlUtil.TryGetAttrValue(node, "url", out val) && !String.IsNullOrEmpty(val))
                        info.SetUrl(val);
                    else if (info.IsHttpRequest)
                        throw new Exception("当isHttpRequest为true时，url属性不能为空");

                    if (XmlUtil.TryGetAttrValue(node, "assembly", out val) && !String.IsNullOrEmpty(val))
                    {
                        string[] names = val.Split(',');
                        foreach (string name in names)
                        {
                            if (!htApiAssemblie.ContainsKey(name))
                                throw new Exception("名称为:" + name + "的assembly配置项不存在");
                            ApiAssemblyInfo assemblyInfo = (ApiAssemblyInfo)htApiAssemblie[name];
                            info.AddAssembly(assemblyInfo);
                        }
                    }
                    else if (info.IsHttpRequest == false)
                        throw new Exception("当isHttpRequest为false时，assembly属性不能为空");

                    if (htApi.ContainsKey(info.AppKey))
                        throw new Exception("存在重复配置项appKey：" + info.AppKey);

                    htApi.Add(info.AppKey, info);

                }

                string def;
                XmlNode xmlNode = xmlDoc.SelectSingleNode("appConfig/apis");
                if (XmlUtil.TryGetAttrValue(xmlNode, "default", out def) && !String.IsNullOrEmpty(def))
                {
                    if (!htApi.ContainsKey(def))
                        throw new Exception("Api默认配置：appKey为" + def + "的Api配置不存在");
                    defaultApiInfo = (ApiInfo)htApi[def];
                }
            }
        }

        public class ApiAssemblyInfo
        {
            private string _name;
            private string _namespace = "";
            private string _typeSuffix = "";
            private string _path;

            public string Name 
            {
                get { return this._name; }
            }

            internal void SetName(string val)
            {
                this._name = val;
            }

            public string Namespace 
            {
                get { return this._namespace; }
            }

            internal void SetNamespace(string val)
            {
                this._namespace = val;
            }

            public string TypeSuffix
            {
                get { return this._typeSuffix; }
            }

            internal void SetTypeSuffix(string val)
            {
                this._typeSuffix = val;
            }

            public string Path 
            {
                get { return this._path; }
            }

            internal void SetPath(string val)
            {
                this._path = val;
            }
        }

        public class ApiInfo
        {
            private string _appKey;
            private bool _isHttpRequest = true;
            private bool _needVerify = true;
            private string _appSecret;
            private int _timestampOffset;
            private string _url;
            private Dictionary<string, ApiAssemblyInfo> dictAssemblies = new Dictionary<string,ApiAssemblyInfo>();
            private List<ApiAssemblyInfo> assemblies = new List<ApiAssemblyInfo>();

            public string AppKey 
            {
                get { return this._appKey; }
            }

            internal void SetAppKey(string val)
            {
                this._appKey = val;
            }

            public bool IsHttpRequest 
            {
                get { return this._isHttpRequest; }
            }

            internal void SetIsHttpRequest(bool val)
            {
                this._isHttpRequest = val;
            }

            public bool NeedVerify 
            {
                get { return this._needVerify; }
            }

            internal void SetNeedVerify(bool val)
            {
                this._needVerify = val;
            }

            public string AppSecret
            {
                get { return this._appSecret; }
            }

            internal void SetAppSecret(string val)
            {
                this._appSecret = val;
            }

            public int TimestampOffset
            {
                get { return this._timestampOffset; }
            }

            internal void SetTimestampOffset(int timestampOffset)
            {
                this._timestampOffset = timestampOffset;
            }

            public string Url 
            {
                get { return this._url; }
            }

            internal void SetUrl(string val)
            {
                this._url = val;
            }

            public ApiAssemblyInfo GetAssemblie(string name)
            {
                ApiAssemblyInfo asmb;
                if (dictAssemblies.TryGetValue(name, out asmb))
                    return asmb;
                else
                    return null;
            }

            public List<ApiAssemblyInfo> Assemblies
            {
                get { return assemblies; }
            }

            internal void AddAssembly(ApiAssemblyInfo info)
            {
                this.dictAssemblies.Add(info.Name, info);
                this.assemblies.Add(info);
            }

        }

    }
}
